#include "stdafx.h"
#include "DefaultGui.h"
#include "resource.h"
#include "OSException.h"
#include "Core.h"
#include "Logging.h"

namespace Lambda = boost::lambda;

struct Page {
    int Index;
    String Name;
    HWND Handle;
    boost::weak_ptr<DefaultGui::Implementation> Worker;
};

struct DefaultGui::Implementation
{
    Core& mCore;
    HWND mFrame;
    std::vector<Page> mPages;
    void AddSinglePage(HWND aParent, PROPSHEETPAGE* aPage);
    void CreatePages(HWND aParent);
    void SetLabelTextForSection(const String& aSection, const String& aLabel, const String& aText);
    void WelcomeSetText(const Page& aPage, const String& aLabel, const String& aText);
    void LicenseSetText(const Page& aPage, const String& aLabel, const String& aText);
    void PasswordSetText(const Page& aPage, const String& aLabel, const String& aText);
    LRESULT WelcomePageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam);
    LRESULT LicensePageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam);
    LRESULT PasswordPageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam);

    Implementation(Core& aCore)
    : mCore(aCore)
    {  }
};

namespace {

boost::shared_ptr<DefaultGui::Implementation> GuiWorker;
const UINT WM_GONEXT = WM_USER + 1;
const UINT WM_GOBACK = WM_USER + 2;

void CenterWizardOnMainDesktop(HWND aHandle)
{
	int Height = GetSystemMetrics(SM_CYSCREEN);
	int Width = GetSystemMetrics(SM_CXSCREEN);

	RECT Rect;
	GetWindowRect(aHandle, &Rect);

	int Left = Width / 2 - (Rect.right - Rect.left) / 2;
	int Top = Height / 2 - (Rect.bottom - Rect.top) / 2;

	MoveWindow(aHandle, Left, Top, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE);
}

void OnceCenterWizardOnMainDesktop(HWND aHandle)
{
	static BOOL Done = FALSE;
	if (!Done) {
		CenterWizardOnMainDesktop(aHandle);
		Done = TRUE;
	}
}

} // anonymous namespace

LRESULT DefaultGui::Implementation::WelcomePageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam)
{
    switch (aMessage)
    {
	case WM_INITDIALOG: {
		OnceCenterWizardOnMainDesktop(GetParent(aPage.Handle));
		HFONT Font = CreateFont(20, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Verdana"));
		HWND Label = GetDlgItem(aPage.Handle, IDC_LABEL_WELCOME_1);
        SendMessage(Label, WM_SETFONT, reinterpret_cast<WPARAM>(Font), TRUE);
		mCore.InitializeSection("Welcome");
        }
		break;
    // on any command notification, tell the property sheet to enable the Apply button
    case WM_COMMAND:
        PropSheet_Changed(GetParent(aPage.Handle), aPage.Handle);
        break;

    case WM_NOTIFY: {
        LPNMHDR MessageHeader = reinterpret_cast<NMHDR*>(aLongParam);

        switch (MessageHeader->code)
        {
            case PSN_APPLY:   //sent when OK or Apply button pressed
                break;

            case PSN_RESET:   //sent when Cancel button pressed
                break;

            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(GetParent(aPage.Handle), PSWIZB_NEXT | PSWIZB_BACK);
                break;

        default:
            break;
        }
    }
        break;

    default:
        break;
    }

    return FALSE;
}

LRESULT DefaultGui::Implementation::LicensePageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam)
{
    switch (aMessage)
    {
	case WM_INITDIALOG:
		OnceCenterWizardOnMainDesktop(GetParent(aPage.Handle));
		mCore.InitializeSection("License");
		CheckRadioButton(aPage.Handle, IDC_RADIO_REJECT, IDC_RADIO_ACCEPT, IDC_RADIO_REJECT);
		break;
    case WM_COMMAND:
        if (HIWORD(aWordParam) == BN_CLICKED) {
            if (LOWORD(aWordParam) == IDC_RADIO_ACCEPT) {
                PropSheet_SetWizButtons(GetParent(aPage.Handle), PSWIZB_BACK | PSWIZB_NEXT);
            }
            else if (LOWORD(aWordParam) == IDC_RADIO_REJECT) {
                PropSheet_SetWizButtons(GetParent(aPage.Handle), PSWIZB_BACK);
            }
        }
        PropSheet_Changed(GetParent(aPage.Handle), aPage.Handle);
        break;

    case WM_NOTIFY: {
        LPNMHDR MessageHeader = reinterpret_cast<NMHDR*>(aLongParam);

        switch (MessageHeader->code) {
            case PSN_SETACTIVE: {
                DWORD Check = PSWIZB_BACK;
                if (Button_GetCheck(GetDlgItem(aPage.Handle, IDC_RADIO_ACCEPT)) == BST_CHECKED) Check |= PSWIZB_NEXT;
                PropSheet_SetWizButtons(GetParent(aPage.Handle), Check);
                }
                break;

            default:
                break;
        }
        }
        break;

    default:
        break;
    }

    return FALSE;
}

LRESULT DefaultGui::Implementation::PasswordPageHandler(const Page& aPage, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam)
{
    switch (aMessage)
    {
	case WM_INITDIALOG:
		OnceCenterWizardOnMainDesktop(GetParent(aPage.Handle));
		mCore.InitializeSection("Password");
		break;
    case WM_COMMAND:
        PropSheet_Changed(GetParent(aPage.Handle), aPage.Handle);
        break;

    case WM_NOTIFY:
        break;

    default:
        break;
    }

    return FALSE;
}

LRESULT CALLBACK PageHandleEvents(HWND aHandle, UINT aMessage, WPARAM aWordParam, LPARAM aLongParam)
{
    try {
        switch (aMessage) {
            case WM_NOTIFY: {
                LPNMHDR MessageHeader = reinterpret_cast<NMHDR*>(aLongParam);
                Page* Item = reinterpret_cast<Page*>(GetWindowLong(aHandle, GWL_USERDATA));

                switch (MessageHeader->code) {
                    case PSN_WIZNEXT:
                        SetWindowLong(Item->Handle, DWL_MSGRESULT, -1);
                        PostMessage(Item->Handle, WM_GONEXT, 0, 0);
                        return TRUE;
                    case PSN_WIZBACK:
                        SetWindowLong(Item->Handle, DWL_MSGRESULT, -1);
                        PostMessage(Item->Handle, WM_GOBACK, 0, 0);
                        return TRUE;
                    default: {
                        boost::shared_ptr<DefaultGui::Implementation> Worker = Item->Worker.lock();
                        switch (Item->Index) {
                            case 1: return Worker->WelcomePageHandler(*Item, aMessage, aWordParam, aLongParam);
                            case 2: return Worker->LicensePageHandler(*Item, aMessage, aWordParam, aLongParam);
                            case 3: return Worker->PasswordPageHandler(*Item, aMessage, aWordParam, aLongParam);
                        }
                    }
                }
            }
            break;
            case WM_GOBACK:
                GuiWorker->mCore.BackCommand();
                break;

            case WM_GONEXT:
                GuiWorker->mCore.NextCommand();
                break;
            case WM_INITDIALOG: {
                PROPSHEETPAGE* Data = reinterpret_cast<PROPSHEETPAGE*>(aLongParam);
                SetWindowLong(aHandle, GWL_USERDATA, Data->lParam);
            }
            default: {
                Page* Item = reinterpret_cast<Page*>(GetWindowLong(aHandle, GWL_USERDATA));

                if (Item != 0) {
                    Item->Handle = aHandle;
                    boost::shared_ptr<DefaultGui::Implementation> Worker = Item->Worker.lock();
                    switch (Item->Index) {
                        case 1: return Worker->WelcomePageHandler(*Item, aMessage, aWordParam, aLongParam);
                        case 2: return Worker->LicensePageHandler(*Item, aMessage, aWordParam, aLongParam);
                        case 3: return Worker->PasswordPageHandler(*Item, aMessage, aWordParam, aLongParam);
                    }
                }
            }
        }
    }
    catch (std::exception& aError) {
        Log("UNHANDLED EXCEPTION!");
        Log(aError.what());
        MessageBox (0, aError.what (), TEXT("Error"), MB_OK);
    }

    return FALSE;
}

void CALLBACK PropSheetCallback(HWND aPropertySheet, UINT uMsg, LPARAM lParam)
{
    switch  (uMsg) {
        case PSCB_INITIALIZED: {
            GuiWorker->mFrame = aPropertySheet;
        }
        break;

        case PSCB_PRECREATE: {
            LPDLGTEMPLATE  lpTemplate = (LPDLGTEMPLATE)lParam;

            if(!(lpTemplate->style & WS_SYSMENU)) {
                lpTemplate->style |= WS_SYSMENU;
            }
        }
        break;
	}
}

DefaultGui::DefaultGui(Core& aCore)
: Ui(aCore), mWorker(new Implementation(aCore))
{
    LoadLibrary(TEXT("Riched20.dll"));
    GuiWorker = mWorker;
}

DefaultGui::~DefaultGui()
{ }

void DefaultGui::Implementation::AddSinglePage(HWND aParent, PROPSHEETPAGE* aPage)
{
    HANDLE Page = CreatePropertySheetPage(aPage);
    if (!PropSheet_AddPage(aParent, Page)) {
        RaiseOsError("Cannot add page");
    }
}

void DefaultGui::Implementation::CreatePages(HWND aParent)
{
    PROPSHEETPAGE Page;
	Page.dwSize        = sizeof(PROPSHEETPAGE);
	Page.dwFlags       = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
	Page.hInstance     = Instance;
	Page.pszTemplate   = MAKEINTRESOURCE(IDD_LICENSE);
	Page.pszIcon       = NULL;
    Page.pfnDlgProc    = (DLGPROC)PageHandleEvents;
	Page.pszTitle      = TEXT("Buttons");
	Page.lParam        = (LPARAM)"License";
    Page.pszHeaderTitle = TEXT("Title");
    Page.pszHeaderSubTitle = TEXT("SubTitle");

	AddSinglePage(aParent, &Page);
}

void DefaultGui::Implementation::SetLabelTextForSection(const String& aSection, const String& aLabel, const String& aText)
{
    auto Item = std::find_if(mPages.cbegin(), mPages.cend(), boost::bind(&Page::Name, Lambda::_1) == aSection);

    if (Item == mPages.cend()) {
        throw std::runtime_error((boost::format("cannot set label for unknown section'%1%'") % aSection).str());
    }

    switch (Item->Index) {
        case 1: WelcomeSetText(*Item, aLabel, aText);
            break;
        case 2: LicenseSetText(*Item, aLabel, aText);
            break;
        case 3: PasswordSetText(*Item, aLabel, aText);
            break;
    }
}

void DefaultGui::Implementation::WelcomeSetText(const Page& aPage, const String& aLabel, const String& aText)
{
    switch (atoi(aLabel.c_str())) {
    case 1: {
        PropSheet_SetHeaderTitle(GuiWorker->mFrame, aPage.Index - 1, aText.c_str());
        break;
    }
    case 2: {
        PropSheet_SetHeaderSubTitle(GuiWorker->mFrame, aPage.Index - 1, aText.c_str());
        break;
    }
    case 3:
        SetDlgItemText(aPage.Handle, IDC_LABEL_WELCOME_1, aText.c_str());
        break;
    case 4:
        SetDlgItemText(aPage.Handle, IDC_LABEL_WELCOME_2, aText.c_str());
        break;
    }
}

void DefaultGui::Implementation::LicenseSetText(const Page& aPage, const String& aLabel, const String& aText)
{
}

void DefaultGui::Implementation::PasswordSetText(const Page& aPage, const String& aLabel, const String& aText)
{
}

void DefaultGui::Run()
{
    mWorker->mPages.resize(3);
	PROPSHEETPAGE psp[3];
	PROPSHEETHEADER psh;

	// Create the welcome page, we need at least one page
	// for the wizard to launch
	mWorker->mPages[0].Index = 1;
	mWorker->mPages[0].Name = mSections[0];
	mWorker->mPages[0].Worker = mWorker;

	psp[0].dwSize        = sizeof(PROPSHEETPAGE);
	psp[0].dwFlags       = PSP_HIDEHEADER;
	psp[0].hInstance     = Instance;
	psp[0].pszTemplate   = MAKEINTRESOURCE(IDD_WELCOME);
	psp[0].pszIcon       = NULL;
    psp[0].pfnDlgProc    = (DLGPROC)PageHandleEvents;
	psp[0].pszTitle      = TEXT("Buttons");
	psp[0].lParam        = reinterpret_cast<LPARAM>(&mWorker->mPages[0]);

	mWorker->mPages[1].Index = 2;
	mWorker->mPages[1].Name = mSections[1];
	mWorker->mPages[1].Worker = mWorker;

	psp[1].dwSize        = sizeof(PROPSHEETPAGE);
	psp[1].dwFlags       = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
	psp[1].hInstance     = Instance;
	psp[1].pszTemplate   = MAKEINTRESOURCE(IDD_LICENSE);
	psp[1].pszIcon       = NULL;
    psp[1].pfnDlgProc    = (DLGPROC)PageHandleEvents;
	psp[1].pszTitle      = TEXT("Buttons");
    psp[1].pszHeaderTitle = TEXT("Title");
    psp[1].pszHeaderSubTitle = TEXT("SubTitle");
	psp[1].lParam        = reinterpret_cast<LPARAM>(&mWorker->mPages[1]);

	mWorker->mPages[2].Index = 3;
	mWorker->mPages[2].Name = mSections[2];
	mWorker->mPages[2].Worker = mWorker;

	psp[2].dwSize        = sizeof(PROPSHEETPAGE);
	psp[2].dwFlags       = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
	psp[2].hInstance     = Instance;
	psp[2].pszTemplate   = MAKEINTRESOURCE(IDD_PASSWORD);
	psp[2].pszIcon       = NULL;
    psp[2].pfnDlgProc    = (DLGPROC)PageHandleEvents;
	psp[2].pszTitle      = TEXT("Buttons");
    psp[2].pszHeaderTitle = TEXT("Title");
    psp[2].pszHeaderSubTitle = TEXT("SubTitle");
	psp[2].lParam        = reinterpret_cast<LPARAM>(&mWorker->mPages[2]);

	//Fill out the PROPSHEETHEADER
	psh.dwSize           = sizeof(PROPSHEETHEADER);
	psh.dwFlags          = PSH_PROPSHEETPAGE | PSH_WIZARD97 | PSH_USECALLBACK | PSH_USEICONID | PSH_WATERMARK | PSH_HEADER;
	psh.hwndParent       = NULL;
	psh.hInstance        = Instance;
	psh.pszIcon          = MAKEINTRESOURCE(IDI_BACKCOLOR);
	psh.pszCaption       = 0;
	psh.nPages           = sizeof(psp) / sizeof(PROPSHEETPAGE);
	psh.ppsp             = (LPCPROPSHEETPAGE) &psp;
    psh.pszbmWatermark   = MAKEINTRESOURCE(IDB_BITMAP1);
    psh.pszbmHeader      = MAKEINTRESOURCE(IDB_BITMAP2);
	psh.pfnCallback      = (PFNPROPSHEETCALLBACK)PropSheetCallback;

	//And finally display the Wizard property sheet

	auto Start = mSections.cbegin();
	auto Stop = mSections.cend();
	int Index = 0;

	while (Start != Stop && mCore.ShouldSkipSection(*Start)) {
        ++Start;
        ++Index;
	}

	if (Start == Stop) {
        throw std::runtime_error("no section can be shown");
	}

	psh.nStartPage = Index;

	PropertySheet(&psh);
}

void DefaultGui::ShowSection (const String& aSection)
{
    int Index = IndexOfSection(aSection);
    if (Index == -1) {
        throw std::runtime_error((boost::format("unknown section '%1%' cannot be shown") % aSection).str());
    }
    PropSheet_SetCurSel(mWorker->mFrame, 0, Index);
}

void DefaultGui::SetLabelTextForSection(const String& aSection, const String& aLabel, const String& aText)
{
    mWorker->SetLabelTextForSection(aSection, aLabel, aText);
}

String DefaultGui::NameOfCurrentSection()
{
    String Result;
    HWND Current = PropSheet_GetCurrentPageHwnd(mWorker->mFrame);

    if (Current != 0) {
        int Index = PropSheet_HwndToIndex(mWorker->mFrame, Current);
        Result = mSections[Index];
    }

    return Result;
}
